package com.mango.community.service;

import com.mango.community.entity.User;
import com.mango.community.util.CommunityConstant;
import com.mango.community.util.RedisKeyUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @Author:  MangoCookieMilk
 * @Date:     2020/8/16 15:42
 *
 * 关注和取关service
 */
@Service
public class FollowService implements CommunityConstant {

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private UserService userService;

    /** 用户关注的方法
     * @param userId
     * @param entityType
     * @param entityId
     */
    public void follow(int userId,int entityType,int entityId){
        redisTemplate.execute(new SessionCallback() {
            @Override
            public Object execute(RedisOperations operations) throws DataAccessException {
                //目标的key
                String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);
                //粉丝的key
                String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityId);
                //开启事务
                operations.multi();
                //将关注的信息存入
                operations.opsForZSet().add(followeeKey,entityId,System.currentTimeMillis());
                //将粉丝的信息存入
                operations.opsForZSet().add(followerKey,userId,System.currentTimeMillis());
                //执行事务
                return operations.exec();
            }
        });
    }

    /** 取消关注的方法
     * @param userId
     * @param entityType
     * @param entityId
     */
    public void UnFollow(int userId,int entityType,int entityId){
        redisTemplate.execute(new SessionCallback() {
            @Override
            public Object execute(RedisOperations operations) throws DataAccessException {
                //目标的key
                String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);
                //粉丝的key
                String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityId);
                //开启事务
                operations.multi();
                //将关注的信息删除
                operations.opsForZSet().remove(followeeKey,entityId);
                //将粉丝的信息删除
                operations.opsForZSet().remove(followerKey,userId);
                //执行事务
                return operations.exec();
            }
        });
    }

    /** 查询关注的实体的数量
     * @param userId
     * @param entityType
     * @return
     */
    public long findFolloweeCount(int userId,int entityType){
        //关注的目标的key
        String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);
        //查询出数量
        return redisTemplate.opsForZSet().zCard(followeeKey);
    }

    /** 查询某个实体的粉丝数量
     * @param entityType
     * @param entityId
     * @return
     */
    public long findFollowerCount(int entityType,int entityId){
        //粉丝的key
        String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityId);
        //查询出数量
        return redisTemplate.opsForZSet().zCard(followerKey);
    }

    /** 查询当前用户是否关注该实体
     * @param userId
     * @param entityType
     * @param entityId
     * @return
     */
    public boolean hasFollowed(int userId,int entityType,int entityId){
        //关注的目标的key
        String followeeKey =  RedisKeyUtil.getFolloweeKey(userId, entityType);
        //通过分数查询他是否关注
        return redisTemplate.opsForZSet().score(followeeKey,entityId) !=null;
    }

    /** 查询某个用户关注的人
     * @param userId
     * @param offset
     * @param limit
     * @return
     */
    public List<Map<String,Object>> findFollowee(int userId,int offset,int limit){
        //关注的目标的key
        String followeeKey =  RedisKeyUtil.getFolloweeKey(userId, ENTITY_TYPE_USER);
        //通过时间的倒叙分页查询出关注的人的id集合
        Set<Integer> targetIds = redisTemplate.opsForZSet().reverseRange(followeeKey, offset, offset + limit - 1);
        //判断空值
        if(targetIds == null ){
            return null;
        }
        List<Map<String,Object>> list = new ArrayList<>();
        //遍历targetIds
        for (Integer targetId : targetIds) {
            Map<String,Object> map = new HashMap<>();
            //根据查询出来的id查询出具体的用户id
            User user = userService.findUserById(targetId);
            //出入到map中
            map.put("user",user);
            //取出targetId 对应的分数时多少，redis中存的就是时间
            Double score = redisTemplate.opsForZSet().score(followeeKey, targetId);
            //因为redis中存入的是时间的毫秒数，所以在获取时间时需要转换一下
            map.put("followTime",new Date(score.longValue()));
            list.add(map);
        }
        return list;
    }

    /** 查询某个用户的粉丝
     * @param userId
     * @param offset
     * @param limit
     * @return
     */
    public List<Map<String,Object>> findFollower(int userId,int offset,int limit){
        //粉丝的key
        String followerKey =  RedisKeyUtil.getFollowerKey(ENTITY_TYPE_USER,userId);
        //通过时间的倒叙分页查询出粉丝的id集合
        Set<Integer> targetIds = redisTemplate.opsForZSet().reverseRange(followerKey, offset, offset + limit - 1);
        if(targetIds ==null){
            return null;
        }
        List<Map<String,Object>> list = new ArrayList<>();
        //遍历targetIds
        for (Integer targetId : targetIds) {
            Map<String,Object> map = new HashMap<>();
            //根据查询出来的id查询出具体的用户id
            User user = userService.findUserById(targetId);
            //出入到map中
            map.put("user",user);
            //取出targetId 对应的分数时多少，redis中存的就是时间
            Double score = redisTemplate.opsForZSet().score(followerKey, targetId);
            //因为redis中存入的是时间的毫秒数，所以在获取时间时需要转换一下
            map.put("followTime",new Date(score.longValue()));
            list.add(map);
        }
        return list;
    }
}
